package com.wangjiang.mario.admin.system.domain.service.impl;

import com.wangjiang.mario.admin.common.constant.JwtConstant;
import com.wangjiang.mario.admin.common.exception.AuthException;
import com.wangjiang.mario.admin.common.util.CommonUtils;
import com.wangjiang.mario.admin.common.util.JwtUtil;
import com.wangjiang.mario.admin.system.domain.components.CacheComponent;
import com.wangjiang.mario.admin.system.domain.entity.UserEntity;
import com.wangjiang.mario.admin.system.domain.service.JwtService;
import com.wangjiang.mario.admin.system.object.token.TokenResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Service
public class JwtServiceImpl implements JwtService {

    private static final Logger logger = LoggerFactory.getLogger(JwtServiceImpl.class);

    @Value("${token_expire}")
    private Integer tokenExpire;

    @Autowired
    private CacheComponent cacheComponent;

    @Autowired
    private HttpServletRequest request;

    /**
     * 根据用户实体生成token
     * <p>
     * 用户uuid作为sub, 其他信息作为map存放在body中
     *
     * @param user
     * @return
     */
    @Override
    public TokenResult generateTokenByUserEntity(UserEntity user) {
        Map body = new HashMap();
        body.put(JwtConstant.JWT_BODY_ID, user.getId());
        body.put(JwtConstant.JWT_BODY_UUID, user.getUuid());
        body.put(JwtConstant.JWT_BODY_LOGINNAME, user.getLoginName());
        body.put(JwtConstant.JWT_BODY_REALNAME, user.getRealName());
        body.put(JwtConstant.JWT_BODY_NICKNAME, user.getNickname());
        body.put(JwtConstant.JWT_BODY_PHONE, user.getPhone());
        body.put(JwtConstant.JWT_BODY_EMAIL, user.getEmail());
        // 生成token
        // 将token存入redis, 设置过期时间
        String token = generateJwtString(user.getUuid(), body);
        logger.info("登录成功，token: {}", token);
        TokenResult tokenResult = new TokenResult();
        tokenResult.setAccess_token(token);
        tokenResult.setExpire_in(tokenExpire);
        return tokenResult;
    }

    @Override
    public String getUUid() {
        String token = getTokenStringFromHeader();
        if (CommonUtils.isNull(token)) {
            throw new AuthException("头部token不存在");
        }
        return getUUid(token);
    }

    @Override
    public String getUUid(String token) {
        token = CommonUtils.isNotNull(token) ? token : getTokenStringFromHeader();
        String uuid = JwtUtil.getSubject(token);

        // 如果缓存中没有则token不合法
        if (!checkIfHasInCache(uuid, token)) {
            throw new AuthException("不合法token");
        }
        return uuid;
    }

    /**
     * 将当前token从token池中剔除
     *
     * @return
     */
    @Override
    public Boolean removeCurrentToken() {
        String token = getTokenStringFromHeader();
        if (JwtUtil.checkJWT(token)) {
            removeTokenFromCache(token);
        } else {
            throw new AuthException("头部token格式不正确");
        }

        return true;
    }


    /**
     *
     * @param uuid
     * @param fullJwtString 带 Bearer
     * @return
     */
    @Override
    public Boolean existsTokenInCache(String uuid, String fullJwtString) {
        return checkIfHasInCache(uuid, fullJwtString);
    }

    @Override
    public UserEntity getUser() {
        String token = getTokenStringFromHeader();
        if (JwtUtil.checkJWT(token)) {
            UserEntity user = new UserEntity();
            user.setUuid((String) JwtUtil.getBody(token).get(JwtConstant.JWT_BODY_UUID));
        } else {
            throw new AuthException("头部token格式不正确");
        }
        return null;
    }

    /**
     * 假定token合法
     * @param token
     */
    private void removeTokenFromCache(String token) {
        Assert.hasLength(token, "token不合法");
        String uuid = JwtUtil.getSubject(token);
        if (checkIfHasInCache(uuid, token)) {
            cacheComponent.remove(JwtConstant.JWT_ACCESS_TOKEN, uuid);
        }
    }

    /**
     * token 有 "Bearer"
     *
     * @param uuid  作为sub
     * @param token
     * @return
     */
    private boolean checkIfHasInCache(String uuid, String token) {
        if (cacheComponent.hasKey(JwtConstant.JWT_ACCESS_TOKEN, uuid)) {
            String cachedToken = (String) cacheComponent.get(JwtConstant.JWT_ACCESS_TOKEN, uuid);
            if (Objects.equals(JwtUtil.JWT_SEPARATOR + cachedToken, token)) return true;
        }
        return false;
    }

    private String getTokenStringFromHeader() {
        String token = request.getHeader(JwtConstant.HEADER_TOKEN_AUTH);
        return token;
    }

    /**
     * 生成jwt 字符串
     * 将token存入redis, 设置过期时间
     *
     * @param sub
     * @param body
     * @return
     */
    private String generateJwtString(String sub, Map body) {
        String token = (String) cacheComponent.get(JwtConstant.JWT_ACCESS_TOKEN, sub);
        if (CommonUtils.isNull(token)) {
            token = generateJwtString(JwtConstant.LOGIN_TOKEN_TYPE, sub, body);
            cacheComponent.put(JwtConstant.JWT_ACCESS_TOKEN, sub, token, tokenExpire);
        }

        return token;
    }

    /**
     * 生成jwt 字符串
     *
     * @param type
     * @param sub
     * @return
     */
    private String generateJwtString(String type, String sub, Map body) {
        if (CommonUtils.isNull(body)) {
            body = new HashMap();
            body.put(JwtConstant.TOKEN_TYPE, type);
        }

        return JwtUtil.buildJWT(sub, body, tokenExpire);
    }
}
